﻿#pragma once

#include "resource.h"
#include "BaseApp.h"
#include "Shader.h"
#include "bMath.h"
#include <sstream>

// 1. 添加引用  LibBase
// 2. 添加 LibBase  的 include path
// 3. C++ 设置为 C++17
// 4. 使用多字节字符集
// 5. 不使用预编译头
// 6. hlsl文件 不参与生成
// 7. 全部使用 utf-8 编码

namespace Base {


	struct VertexPosColor {
		Vec3 pos;
		Vec4 color;
	};

	struct ConstantBuffer {
		Matrix4x4 world;
		Matrix4x4 view;
		Matrix4x4 proj;
	};

	class MyApp :public BaseApp {
	public:
		void InitResources() {
			auto graphics = GetGraphics();
			auto device = graphics->GetDevice();
			auto context = graphics->GetDeviceContext();

			// 初始化  shader  和 layout
			m_shader = std::make_unique<Shader>();
			m_shader->Init("first.hlsl", "VS_Main", "PS_Main");
			auto vertexShader = m_shader->GetVertexShader();
			auto pixelShader = m_shader->GetPixelShader();
			auto vsBuffer = m_shader->GetVertexShaderBuffer();

			// create input layout
			const D3D11_INPUT_ELEMENT_DESC inputLayout[2] = {
				{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
				{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, sizeof(Vec3), D3D11_INPUT_PER_VERTEX_DATA, 0 }
			};
			unsigned int totalLayoutElements = ARRAYSIZE(inputLayout);
			D_HRESULT(device->CreateInputLayout(
				inputLayout,
				totalLayoutElements,
				vsBuffer->GetBufferPointer(),
				vsBuffer->GetBufferSize(),
				&m_inputLayout));

			// ******************
			// 设置立方体顶点
			//    5________ 6
			//    /|      /|
			//   /_|_____/ |
			//  1|4|_ _ 2|_|7
			//   | /     | /
			//   |/______|/
			//  0       3
			VertexPosColor vertices[] =
			{
				{ Vec3(-1.0f, -1.0f, -1.0f), Vec4(0.0f, 0.0f, 0.0f, 1.0f) },
				{ Vec3(-1.0f, 1.0f, -1.0f), Vec4(1.0f, 0.0f, 0.0f, 1.0f) },
				{ Vec3(1.0f, 1.0f, -1.0f), Vec4(1.0f, 1.0f, 0.0f, 1.0f) },
				{ Vec3(1.0f, -1.0f, -1.0f), Vec4(0.0f, 1.0f, 0.0f, 1.0f) },
				{ Vec3(-1.0f, -1.0f, 1.0f), Vec4(0.0f, 0.0f, 1.0f, 1.0f) },
				{ Vec3(-1.0f, 1.0f, 1.0f), Vec4(1.0f, 0.0f, 1.0f, 1.0f) },
				{ Vec3(1.0f, 1.0f, 1.0f), Vec4(1.0f, 1.0f, 1.0f, 1.0f) },
				{ Vec3(1.0f, -1.0f, 1.0f), Vec4(0.0f, 1.0f, 1.0f, 1.0f) }
			};

			// 设置顶点缓冲区描述
			D3D11_BUFFER_DESC vbd = {};
			vbd.Usage = D3D11_USAGE_IMMUTABLE;
			vbd.ByteWidth = sizeof(vertices);
			vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;

			// 新建顶点缓冲区
			D3D11_SUBRESOURCE_DATA InitData = {};
			InitData.pSysMem = vertices;
			D_HRESULT(device->CreateBuffer(&vbd, &InitData, m_vertexBuffer.GetAddressOf()));

			// ******************
			// 索引数组
			//
			unsigned int indices[] = {
				// 正面
				0, 1, 2,
				2, 3, 0,
				// 左面
				4, 5, 1,
				1, 0, 4,
				// 顶面
				1, 5, 6,
				6, 2, 1,
				// 背面
				7, 6, 5,
				5, 4, 7,
				// 右面
				3, 2, 6,
				6, 7, 3,
				// 底面
				4, 0, 3,
				3, 7, 4
			};
			// 设置索引缓冲区描述
			D3D11_BUFFER_DESC ibd = {};
			ibd.Usage = D3D11_USAGE_IMMUTABLE;
			ibd.ByteWidth = sizeof indices;
			ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;

			// 新建索引缓冲区
			InitData.pSysMem = indices;
			D_HRESULT(device->CreateBuffer(&ibd, &InitData, &m_indexBuffer));

			// ******************
			// 设置常量缓冲区描述
			D3D11_BUFFER_DESC cbd = {};
			cbd.Usage = D3D11_USAGE_DYNAMIC;
			cbd.ByteWidth = sizeof(ConstantBuffer);
			cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
			cbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
			// 新建常量缓冲区，不使用初始数据
			D_HRESULT(device->CreateBuffer(&cbd, nullptr, &m_constantBuffer));


			// 初始化常量缓冲区的值
			m_cbuffer.world.SetIdentity();
			m_cbuffer.view = Matrix4x4::CreateTranslation(0, 0, 5).Transpose();
			float aspect = (float)GetWidth() / GetHeight();
			m_cbuffer.proj = Matrix4x4::CreatePerspectiveFovLH(Deg2Rad(60), aspect, 1.0f, 1000.0f).Transpose();


			// ******************
			// 给渲染管线各个阶段绑定好所需资源
			// 输入装配阶段的顶点缓冲区设置
			UINT stride = sizeof(VertexPosColor);	// 跨越字节数
			UINT offset = 0;						// 起始偏移量

			// 装配 顶点缓存 索引缓存
			context->IASetVertexBuffers(0, 1, m_vertexBuffer.GetAddressOf(), &stride, &offset);
			context->IASetIndexBuffer(m_indexBuffer.Get(), DXGI_FORMAT_R32_UINT, 0);

			// 设置图元类型和布局
			context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
			context->IASetInputLayout(m_inputLayout.Get());

			// 将着色器绑定到渲染管线
			context->VSSetShader(vertexShader, nullptr, 0);

			// 将更新好的常量缓冲区绑定到顶点着色器
			context->VSSetConstantBuffers(0, 1, m_constantBuffer.GetAddressOf());

			context->PSSetShader(pixelShader, nullptr, 0);

			// ******************
			// 设置调试对象名
			/*D3D11SetDebugObjectName(m_pVertexLayout.Get(), "VertexPosColorLayout");
			D3D11SetDebugObjectName(m_pVertexBuffer.Get(), "VertexBuffer");
			D3D11SetDebugObjectName(m_pIndexBuffer.Get(), "IndexBuffer");
			D3D11SetDebugObjectName(m_pConstantBuffer.Get(), "ConstantBuffer");
			D3D11SetDebugObjectName(m_pVertexShader.Get(), "Cube_VS");
			D3D11SetDebugObjectName(m_pPixelShader.Get(), "Cube_PS");*/
		}

		virtual void OnInit() {
			r = g = b = 0;
			InitResources();
		}

		void OnInput() {

			// 键盘和字符事件
			while (const auto e = m_keyboard->ReadKey())
			{
				std::stringstream  ss;
				ss << "received :" << e->GetCode() << " " << (e->IsPressed() ? "down" : "up");
				Info(ss.str());
			}

			while (const auto e = m_keyboard->ReadChar())
			{
				std::stringstream  ss;
				ss << "received char:" << e.value();
				Info(ss.str());
			}

			// 鼠标事件
			while (const auto e = m_mouse->Read())
			{
				switch (e->GetType())
				{
				case MouseEvent::Type::LPress:
					Info("left mouse pressed");
					break;
				case MouseEvent::Type::LRelease:
					Info("left mouse release");
					break;
				case MouseEvent::Type::RPress:
					Info("right mouse pressed");
					break;
				case MouseEvent::Type::RRelease:
					Info("right mouse release");
					break;
				case MouseEvent::Type::Move:
				{
					std::stringstream ss;
					ss << "mouse move. pos: <" << e->GetX() << "," << e->GetY() << ">";
					Info(ss.str());
				}
				break;
				case MouseEvent::Type::Enter:
					Info("mouse enter");
					break;
				case MouseEvent::Type::Leave:
					Info("mouse leave");
					break;
				default:
					break;
				}
			}
		}

		virtual void OnFrame() {
			auto graphics = GetGraphics();
			auto context = graphics->GetDeviceContext();

			graphics->ClearBuffer();

			// do render
			// 绘制立方体
			context->DrawIndexed(36, 0, 0);

			graphics->Present();
		}
		virtual void OnTerminate() {

		}
		void OnUpdate() {

			std::stringstream ss;
			ss << "fps:" << m_timer->GetFPS();
			SetWindowText(m_hWnd, ss.str().c_str());

			static float phi = 0.0f, theta = 0.0f;
			phi += 0.001f, theta += 0.0015f;

			// 更新 worldMatrix
			m_cbuffer.world = Matrix4x4::CreateRotation(phi, theta, 0).Transpose();

			auto graphics = GetGraphics();
			auto context = graphics->GetDeviceContext();

			// 更新常量缓冲区，让立方体转起来
			D3D11_MAPPED_SUBRESOURCE mappedData;
			D_HRESULT(context->Map(m_constantBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedData));
			memcpy_s(mappedData.pData, sizeof(m_cbuffer), &m_cbuffer, sizeof(m_cbuffer));
			context->Unmap(m_constantBuffer.Get(), 0);
		}

	private:
		float r, g, b;


		ComPtr<ID3D11Buffer> m_vertexBuffer; // 顶点缓冲
		ComPtr<ID3D11Buffer> m_indexBuffer;  // 索引缓冲
		ComPtr<ID3D11Buffer> m_constantBuffer; // 常量缓冲

		ConstantBuffer m_cbuffer;

		// 
		ComPtr<ID3D11InputLayout> m_inputLayout;
		std::unique_ptr<Shader> m_shader;

	};
}